package com.milloc.db.bo;

import com.alibaba.fastjson.JSON;
import com.milloc.db.common.ParameterType;
import com.milloc.db.common.ScriptType;
import com.milloc.db.entity.ParameterEntity;
import com.milloc.db.entity.ScriptEntity;
import com.milloc.db.service.SQLRunnerService;
import com.milloc.db.service.SQLService;
import com.milloc.db.service.ScriptService;
import com.milloc.db.wrapper.RunnerWrapper;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.toList;

/**
 * @author gongdeming
 * @create 2019-08-30
 */
@Component
public class JavaScriptBO extends ScriptBO {
    private ScriptEngine jsEngine;

    public JavaScriptBO(SQLService sqlService, SQLRunnerService sqlRunnerService, ScriptService scriptService) {
        jsEngine = new ScriptEngineManager().getEngineByName("js");
        jsEngine.put("$", new RunnerWrapper(sqlRunnerService, this, sqlService, scriptService));
    }

    @Override
    public boolean supports(ScriptEntity scriptEntity) {
        return scriptEntity.getScriptType() == ScriptType.JAVASCRIPT;
    }

    @Override
    public List<ParameterEntity> loadParameter(ScriptEntity scriptEntity) {
        String scriptBody = scriptEntity.getScriptBody();

        Pattern pattern = Pattern.compile("(?=[\\W\\s])" + ScriptEntity.ARGS_NAME + "\\.([\\w_.]+)");
        Matcher matcher = pattern.matcher(scriptBody);
        Set<String> parameters = new HashSet<>();
        while (matcher.find()) {
            parameters.add(matcher.group(2));
        }
        return parameters.stream()
                .filter(StringUtils::isNotBlank)
                .map(s -> s.contains(".") ? StringUtils.substringBefore(s, ".") + "." : s)
                .distinct()
                .sorted()
                .map(s -> {
                    ParameterEntity p = ParameterEntity.of(StringUtils.substringBefore(s, "."));
                    if (s.contains(".")) {
                        p.setType(ParameterType.OBJECT);
                    }
                    return p;
                })
                .collect(toList());
    }

    @Override
    @SneakyThrows
    public Object run(ScriptEntity scriptEntity, Map<String, Object> params) {
        jsEngine.eval(scriptEntity.getScriptBody());
        String script = ScriptEntity.SCRIPT_FUNCTION + "(" + JSON.toJSONString(params) + ")";
        return jsEngine.eval(script);
    }
}
